﻿using System;
using System.Collections.Generic;
using System.Web;
using System.IO;

public class UploadHandler : Handler {

	public UploadConfig UploadConfig { get; private set; }
	public UploadResult Result { get; private set; }

	public UploadHandler(HttpContext context, UploadConfig config) : base(context) {
		this.UploadConfig = config;
		this.Result = new UploadResult() { State = UploadState.Unknown };
	}

	public override void Process() {
		byte[] uploadFileBytes = null;
		string uploadFileName = null;

		if (UploadConfig.Base64) {
			uploadFileName = UploadConfig.Base64Filename;
			uploadFileBytes = Convert.FromBase64String(Request[UploadConfig.UploadFieldName]);
		} else {
			var file = Request.Files[UploadConfig.UploadFieldName];
			uploadFileName = file.FileName;

			if (!CheckFileType(uploadFileName)) {
				Result.State = UploadState.TypeNotAllow;
				WriteResult();
				return;
			}
			if (!CheckFileSize(file.ContentLength)) {
				Result.State = UploadState.SizeLimitExceed;
				WriteResult();
				return;
			}

			uploadFileBytes = new byte[file.ContentLength];
			try {
				file.InputStream.Read(uploadFileBytes, 0, file.ContentLength);
			} catch (Exception) {
				Result.State = UploadState.NetworkError;
				WriteResult();
			}
		}

		Result.OriginFileName = uploadFileName;

		var savePath = PathFormatter.Format(uploadFileName, UploadConfig.PathFormat);
		var localPath = Server.MapPath(savePath);
		try {
			if (!Directory.Exists(Path.GetDirectoryName(localPath))) {
				Directory.CreateDirectory(Path.GetDirectoryName(localPath));
			}
			File.WriteAllBytes(localPath, uploadFileBytes);
			Result.Url = savePath;
			Result.State = UploadState.Success;
		} catch (Exception e) {
			Result.State = UploadState.FileAccessError;
			Result.ErrorMessage = e.Message;
		} finally {
			WriteResult();
		}
	}

	private void WriteResult() {
		Dictionary<string, string> _item = new Dictionary<string, string>();
		_item.Add("state",GetStateMessage(Result.State));
		_item.Add("url", Result.Url);
		_item.Add("title", Result.OriginFileName);
		_item.Add("original", Result.OriginFileName);
		_item.Add("error", Result.ErrorMessage);
		this.WriteJson(LitJson.JsonMapper.ToObject(LitJson.JsonMapper.ToJson(_item)));
	}

	private string GetStateMessage(UploadState state) {
		switch (state) {
			case UploadState.Success:
				return "SUCCESS";
			case UploadState.FileAccessError:
				return "文件访问出错，请检查写入权限";
			case UploadState.SizeLimitExceed:
				return "文件大小超出服务器限制";
			case UploadState.TypeNotAllow:
				return "不允许的文件格式";
			case UploadState.NetworkError:
				return "网络错误";
		}
		return "未知错误";
	}

	private bool CheckFileType(string filename) {
		var fileExtension = Path.GetExtension(filename).ToLower();

		foreach (string _s in UploadConfig.AllowExtensions) {
			if (filename.ToLower() == _s.ToLower()) {
				return _s.ToLower().Contains(fileExtension)?false:true;
			}
		}
		return true;
		//return UploadConfig.AllowExtensions.Select(x => x.ToLower()).Contains();
	}

	private bool CheckFileSize(int size) {
		return size < UploadConfig.SizeLimit;
	}
}

public class UploadConfig {
	//.net2.0没有直接new UploadConfig{ AllowExtensions = Config.GetStringList("imageAllowFiles"),... }的方法，所以自己做一个构造函数实现
	public UploadConfig(string[] AllowExtensions, string PathFormat, int SizeLimit, string UploadFieldName) {
		this.SetConfig(AllowExtensions, PathFormat, SizeLimit, UploadFieldName, false, null);
	}

	public UploadConfig(string[] AllowExtensions, string PathFormat, int SizeLimit, string UploadFieldName, bool Base64, string Base64Filename) {
		this.SetConfig(AllowExtensions,PathFormat,SizeLimit,UploadFieldName,Base64,Base64Filename);
	}

	private void SetConfig(string[] AllowExtensions, string PathFormat, int SizeLimit, string UploadFieldName, bool Base64, string Base64Filename) {
		this.AllowExtensions = AllowExtensions;
		this.PathFormat = PathFormat;
		this.SizeLimit = SizeLimit;
		this.UploadFieldName = UploadFieldName;
		this.Base64 = Base64;
		this.Base64Filename = null == Base64Filename ? null : Base64Filename;
	}

	/// <summary>
	/// 文件命名规则
	/// </summary>
	public string PathFormat { get; set; }

	/// <summary>
	/// 上传表单域名称
	/// </summary>
	public string UploadFieldName { get; set; }

	/// <summary>
	/// 上传大小限制
	/// </summary>
	public int SizeLimit { get; set; }

	/// <summary>
	/// 上传允许的文件格式
	/// </summary>
	public string[] AllowExtensions { get; set; }

	/// <summary>
	/// 文件是否以 Base64 的形式上传
	/// </summary>
	public bool Base64 { get; set; }

	/// <summary>
	/// Base64 字符串所表示的文件名
	/// </summary>
	public string Base64Filename { get; set; }
}

public class UploadResult {
	public UploadState State { get; set; }
	public string Url { get; set; }
	public string OriginFileName { get; set; }

	public string ErrorMessage { get; set; }
}

public enum UploadState {
	Success = 0,
	SizeLimitExceed = -1,
	TypeNotAllow = -2,
	FileAccessError = -3,
	NetworkError = -4,
	Unknown = 1,
}